import java.util.*;
import java.io.*;
import java.text.*;
import javax.swing.*;
/**
 * Used for handling the generation of the report when
 * the method of creation is the GUI (either wizard or
 * expert mode).
 * 
 * @author James Stanier
 */
public class OutputHandler {

	ArrayList<File> inputFiles;

	ArrayList<String> inDepthSections;
	ArrayList<ArrayList<String>> codeIndexes;
	ArrayList<String> finalOutput;

	String latexPackages;

	ExpertGUI egui;
	TitleGUI tgui;
	ReviewGUI rgui;

	JFrame frame;
	JFrame prev;
	
	String outputDir;
	String fileName;
	String paperType;
	String docType;
	String variableOutput;
	String author;
	String title;
	int fontSize;
	boolean titlePage;
	boolean inDepthSection;
	boolean tableOfContents;
	boolean keywordIndex;
	boolean codeIndex;
	boolean twoSidedPrinting;

	/**
	 * Constructor.
	 * @param o The GUI object we have arrived here from.
	 * @param a The list of input files.
	 * @param j The outer window of the previous GUI screen.
	 */
	public OutputHandler(Object o, ArrayList<File> a, JFrame j) {

		frame = j;

		if(o instanceof ExpertGUI) {
			egui = (ExpertGUI)o;
			paperType = egui.getPaperType();
			docType = egui.getDocType();
			variableOutput = egui.getVariableOutputOpt();
			fontSize = egui.getFontSize();
			titlePage = egui.isTitlePage();
			inDepthSection = egui.isInDepthSection();
			tableOfContents = egui.isTableOfContents();
			keywordIndex = egui.isKeywordIndex();
			codeIndex = egui.isCodeIndex();
			twoSidedPrinting = egui.isTwoSidedPrinting();
			outputDir = egui.getOutputDir();
			fileName = egui.getOutputFileName();
			prev = egui.getWindow();
		}
		else if(o instanceof TitleGUI) {
			tgui = (TitleGUI)o;
			egui = tgui.getExpertGUI();
			paperType = egui.getPaperType();
			docType = egui.getDocType();
			variableOutput = egui.getVariableOutputOpt();
			fontSize = egui.getFontSize();
			titlePage = egui.isTitlePage();
			inDepthSection = egui.isInDepthSection();
			tableOfContents = egui.isTableOfContents();
			keywordIndex = egui.isKeywordIndex();
			codeIndex = egui.isCodeIndex();
			twoSidedPrinting = egui.isTwoSidedPrinting();
			outputDir = egui.getOutputDir();
			author = tgui.getAuthor();
			title = tgui.getTitle();
			fileName = egui.getOutputFileName();
			prev = egui.getWindow();
		}
		else if(o instanceof ReviewGUI) {
			rgui = (ReviewGUI)o;
			paperType = rgui.getPaperType();
			docType = "article";
			variableOutput = rgui.getVariableOutput();
			fontSize = rgui.getFontSize();
			titlePage = rgui.isTitlePage();
			inDepthSection = rgui.isInDepthSection();
			tableOfContents = rgui.isTableOfContents();
			keywordIndex = rgui.isKeywordIndex();
			codeIndex = rgui.isCodeIndex();
			twoSidedPrinting = rgui.isTwoSidedPrinting();
			outputDir = rgui.getOutputDir();
			author = rgui.getAuthor();
			title = rgui.getTitle();
			fileName = rgui.getOutputFileName();
			prev = rgui.getWindow();
		}

		inputFiles = a;

		inDepthSections = new ArrayList<String>();
		codeIndexes = new ArrayList<ArrayList<String>>();
		finalOutput = new ArrayList<String>();

		latexPackages = "\\usepackage{geometry}\n" +
		"\\usepackage{amssymb}\n" +
		"\\usepackage{epstopdf}\n" +
		"\\usepackage{longtable}\n" +
		"\\usepackage[usenames]{color}\n" +
		"\\usepackage[parfill]{parskip}";

		generateDocument();
	}

	/**
	 * Takes the user input and then generates the report.
	 */
	public void generateDocument() {

		String temp = "";
		if(twoSidedPrinting) {
			temp = ",twoside";
		}

		finalOutput.add("\\documentclass[" + fontSize + "pt" + temp + "]{" + 
				docType + "}\n");
		finalOutput.add(latexPackages);
		finalOutput.add("\\geometry{" + paperType + "}\n");


		if(titlePage) {
			finalOutput.add("\\begin{titlepage}\n");
			finalOutput.add("\\title{" + title + "}\n");
			finalOutput.add("\\author{" + author + "}\n");
			finalOutput.add("\\end{titlepage}\n");
		}

		finalOutput.add("\\begin{document}\n");

		if(titlePage) {
			finalOutput.add("\\maketitle\n");
		}

		if(tableOfContents) {
			finalOutput.add("\\tableofcontents\n");
		}



		try {
			boolean cont = true;
			finalOutput.add("\\newpage\n");

			for(int i = 0; i < inputFiles.size(); i++) {

				File f = inputFiles.get(i);
				String[] s = new String[1];
				s[0] = f.getAbsolutePath();


				// Parsing the source file
				try {
					JavaParser.main(s);
				}
				catch (Exception e) {
					JOptionPane.showMessageDialog(frame, "The Java file " + f.getName() + " failed parsing." +
							" \nPlease check that it is a valid Java file that can be compiled \nwith javac, and" +
					" then try again.");
					cont = false;
				}

				if(!cont) {
					returnToPrev();
				}
				else {

					// Traversing for in-depth comments
					Node tree = JavaParser.getTree();
					tree.traverseForInDepthComments(tree);
					ArrayList<CommentToken> inDepthComments = JavaParser.getInDepthComments();
					Collections.sort(inDepthComments);

					String varListOpt = "none";
					boolean isVarOutput = false;
					// Generating in-depth content and keywords
					if(variableOutput.equals("index")) {
						isVarOutput = true;
						varListOpt = "index";
					}
					else if(variableOutput.equals("grid")) {
						isVarOutput = true;
						varListOpt = "grid";
					}

					InDepthToLaTeX id2l = new InDepthToLaTeX(inDepthComments, JavaParser.getFileName(),
							JavaParser.createVariableOutput(varListOpt));
					try {
						id2l.prepareOutput();
					}
					catch(Exception e) {
						if(e.getMessage().equals("1")) {
							JOptionPane.showMessageDialog(frame, "The Java file " + f.getName() + " contained " +
									"a section heading without a closing * character.\n" +
							"Please correct this, then try again.");
						}
						else if(e.getMessage().equals("2")) {
							JOptionPane.showMessageDialog(frame, "The Java file " + f.getName() + " contained " +
									"a math-mode mark-up without a closing $ character.\n" +
							"Please correct this, then try again.");
						}
						else if(e.getMessage().equals("3")) {
							JOptionPane.showMessageDialog(frame, "The Java file " + f.getName() + " contained " +
									"a keyword-mode mark-up without a closing | character.\n" +
							"Please correct this, then try again.");
						}
						else {
							JOptionPane.showMessageDialog(frame, "The Java file " + f.getName() + " contained " +
									"an invalid comment.\n" +
							"Please correct this, then try again.");
						}
						cont = false;
					}

					if(!cont) {
						returnToPrev();
					}
					else {

						ArrayList<LaTeXCommentToken> transOut = id2l.getTranslatedOutput();

						// Create in-depth section
						InDepthSectionCreator idsc = new InDepthSectionCreator(JavaParser.getFileName(), transOut);
						try {
							idsc.insertCode();
						}
						catch(Exception e) {
							JOptionPane.showMessageDialog(frame, "The Java file " + f.getName() + " contained " +
									"Javalin comments in the wrong order. \n!shut found before !open. \nPlease " +
							"correct this and then try again.");
							cont = false;
						}

						if(!cont) {
							returnToPrev();
						}
						else {

							if(!varListOpt.equals("none")) {
								idsc.addVariableIndex(JavaParser.createVariableOutput(varListOpt));
							}

							// Add to storage
							if(inDepthSection && keywordIndex) {
								addInDepthBundle(idsc.getOutput(), id2l.getTranslatedKeywords());
							}
							else if(inDepthSection && !keywordIndex) {
								addInDepthBundle(idsc.getOutput());
							}
							else if(!inDepthSection && keywordIndex) {
								addInDepthBundle(id2l.getTranslatedKeywords());
							}

							// Code index
							if(codeIndex) {
								CodeIndexGenerator cig = new CodeIndexGenerator(JavaParser.getFileName(), 
										new GridOutput(JavaParser.getSymbolTable(),
												JavaParser.getFileName()).generateStringArray());
								addCodeIndex(cig.generateIndex());
							}
						}
					}
				}
			}

			if(cont) {

				// Add in-depth sections to final array
				for(int i = 0; i < inDepthSections.size(); i++) {
					finalOutput.add(inDepthSections.get(i));
				}

				for(int i = 0; i < codeIndexes.size(); i++) {
					ArrayList<String> x = codeIndexes.get(i);
					for(int j = 0; j < x.size(); j++) {
						finalOutput.add(x.get(j));
					}
				}

				finalOutput.add("\n\\end{document}\n");

				writeToFile();
				
				JOptionPane.showMessageDialog(frame, "Output written to file successfully.");
				resetGUI();

			}
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Write the generated document to the file in the location that the user
	 * has specified.
	 */
	public void writeToFile() {
		try {
			FileWriter fstream = new FileWriter(outputDir + File.separator + 
					fileName + ".tex");
			BufferedWriter out = new BufferedWriter(fstream);

			for(int i = 0; i < finalOutput.size(); i++) {
				out.write(finalOutput.get(i));
			}

			out.close();
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Return to the previous GUI window.
	 */
	public void returnToPrev() {
		prev.setVisible(true);
	}

	/**
	 * Dump the generated document to the terminal window.
	 */
	public void dumpToConsole() {

		for(int i = 0; i < finalOutput.size(); i++) {
			System.out.print(finalOutput.get(i));
		}

	}

	/**
	 * Bundles together keyword index and variable grid/index.
	 * @param s Keyword index.
	 * @param t Variable index.
	 */
	public void addInDepthBundle(String s, String t) {
		inDepthSections.add(s);
		inDepthSections.add(t);
	}

	/**
	 * Stores a keyword or variable index/grid.
	 * @param s Keyword or variable index/grid to be added.
	 */
	public void addInDepthBundle(String s) {
		inDepthSections.add(s);
	}

	/**
	 * Adds a code index to the output.
	 * @param s Code index to be added.
	 */
	public void addCodeIndex(ArrayList<String> s) {
		codeIndexes.add(s);
	}
	
	public void resetGUI() {
		
		frame.setVisible(false);
		GUI.main(new String[0]);

	}

}
